package lexer

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
	"unicode"

	"jane-db/pkg/util/time"
)

// Lexer 词法分析器
type Lexer struct {
	reader   *bufio.Reader
	currChar rune // 当前字符
}

func New(reader io.Reader) (*Lexer, error) {
	l := &Lexer{
		reader: bufio.NewReader(reader),
	}
	if err := l.readChar(); err != nil {
		return nil, err
	}
	return l, nil
}

// NextToken 获取下一个 token
func (l *Lexer) NextToken() (*Token, error) {
	// 跳过空格
	if err := l.skipWhitespace(); err != nil {
		return nil, err
	}
	// 读取 token
	t, err := l.readToken()
	if err != nil {
		return nil, err
	}
	// 读取换行符
	if err := l.readChar(); err != nil {
		return nil, err
	}

	return t, nil
}

func (l *Lexer) readChar() error {
	ch, _, err := l.reader.ReadRune()
	if err != nil {
		l.currChar = 0
		return fmt.Errorf("lexer reading char error: %v", err)
	}

	l.currChar = ch
	return nil
}

func (l *Lexer) peekChar() (rune, error) {
	next, err := l.reader.Peek(1)
	if err != nil {
		return 0, fmt.Errorf("lexer peeking char error: %v", err)
	}
	return rune(next[0]), nil
}

func (l *Lexer) skipWhitespace() error {
	for unicode.IsSpace(l.currChar) {
		if err := l.readChar(); err != nil {
			return err
		}
	}
	return nil
}

func (l *Lexer) readToken() (*Token, error) {
	switch l.currChar {
	case 0:
		return TOKEN_EOF, nil

	case '#':
		return l.readComment()

	case '=', '<', '>', '!':
		return l.readOperator()

	case ',', ';', '(', ')', '*':
		return l.readToolSym()

	case '.':
		return l.readDot()

	case '\'':
		return l.readStringLiteral()

	default:
		return l.readOther()
	}
}

func (l *Lexer) readComment() (*Token, error) {
	// 跳过 '#' 字符
	if err := l.readChar(); err != nil {
		return nil, err
	}
	// 读取整行内容
	var comment bytes.Buffer
	for !l.isLineEnd() {
		comment.WriteRune(l.currChar)
		if err := l.readChar(); err != nil {
			return nil, err
		}
	}

	return &Token{Type: COMMENT, Literal: comment.String()}, nil
}

func (l *Lexer) readOperator() (*Token, error) {
	var operator bytes.Buffer
	operator.WriteRune(l.currChar)
	if err := l.readChar(); err != nil {
		return nil, err
	}

	// 检查当前操作符是否为双字符操作符，如果是，则需要补全
	if l.currChar == '=' {
		operator.WriteRune(l.currChar)
		if err := l.readChar(); err != nil {
			return nil, err
		}
	}

	literal := operator.String()
	t := operatorMap[literal]
	return t, nil
}

func (l *Lexer) readToolSym() (*Token, error) {
	toolSym := l.currChar
	if err := l.readChar(); err != nil {
		return nil, err
	}

	t := toolSymMap[toolSym]
	return t, nil
}

func (l *Lexer) readDot() (*Token, error) {
	var t *Token
	peekChar, err := l.peekChar()
	if err != nil {
		return nil, err
	}
	if unicode.IsDigit(peekChar) {
		// 如果 '.' 是小数的一部分，这里的小数只能以 '.' 开头，类似于 ".101"
		t = new(Token)

		numString, err := l.readNumberAsString()
		if err != nil {
			return nil, err
		}
		if strings.Contains(numString, ".") {
			t.Type = FLOAT
		} else {
			t.Type = INT
		}

		t.Literal = numString
	} else {
		// 否则 '.' 只能表示 表名.列名
		t = TOKEN_DOT
		if err := l.readChar(); err != nil {
			return nil, err
		}
	}
	return t, nil
}

func (l *Lexer) readStringLiteral() (*Token, error) {
	var str bytes.Buffer
	// 跳过开始的引号
	if err := l.readChar(); err != nil {
		return nil, err
	}
	// 读取被单引号包裹起来的字符串
	for l.currChar != '\'' && l.currChar != 0 {
		str.WriteRune(l.currChar)
		if err := l.readChar(); err != nil {
			return nil, err
		}
	}
	// 跳过结束的引号
	if err := l.readChar(); err != nil {
		return nil, err
	}

	literal := str.String()
	var tokenType TokenType
	if time.IsDateTimeFormat(literal) {
		tokenType = DATETIME
	} else {
		tokenType = STRING
	}

	return &Token{Type: tokenType, Literal: literal}, nil
}

func (l *Lexer) readOther() (*Token, error) {
	var t Token
	if unicode.IsLetter(l.currChar) {
		// 如果是字符，则读取标识符
		identifier, err := l.readIdentifier()
		if err != nil {
			return nil, err
		}

		// 如果是关键字，则直接返回关键字单例
		keyword, ok := keywordMap[identifier]
		if ok {
			return keyword, nil
		}

		// 否则是标识符
		t.Type = IDENT
		t.Literal = identifier
	} else if unicode.IsDigit(l.currChar) {
		// 如果是数字，则读取数字
		num, err := l.readNumberAsString()
		if err != nil {
			return nil, err
		}

		if strings.Contains(num, ".") {
			// 如果是小数
			t.Type = FLOAT
		} else {
			// 否则是整数
			t.Type = INT
		}

		t.Literal = num
	} else {
		// 否则格式错误，返回错误
		return nil, fmt.Errorf("invalid format: start with %c", l.currChar)
	}
	return &t, nil
}

// 将数字读取成字符串，支持小数和整数，读取完之后会将 l.currChar 调整到下一个字符
func (l *Lexer) readNumberAsString() (string, error) {
	var num bytes.Buffer

	// 读取 '.' 的下一个字符
	peekChar, err := l.peekChar()
	if err != nil {
		return "", err
	}

	// 如果一个字符串以 '.' 开头，但是下一个字符却不是数字，那么这个字符串不是数字，返回错误
	if l.currChar == '.' && !unicode.IsDigit(peekChar) {
		return "", fmt.Errorf("invalid decimal: "+
			"decimal starting with a decimal point "+
			"must be followed by digits, got '%c' instead", peekChar)
	}

	hasDecimalPoint := false
	for unicode.IsDigit(l.currChar) || l.currChar == '.' {
		if l.currChar == '.' {
			if hasDecimalPoint {
				return "", fmt.Errorf("invalid decimal: "+
					"decimal cannot have two decimal point, "+
					"decimal before second decimal point is %s", num.String())
			}
			hasDecimalPoint = true
		}
		num.WriteRune(l.currChar)
		l.readChar()
	}

	return num.String(), nil
}

// 读取标识符，读取完之后会将 l.currChar 调整到下一个字符
func (l *Lexer) readIdentifier() (string, error) {
	var identifier bytes.Buffer

	// 读取被单引号包裹起来的字符串
	for l.currChar != '\'' && l.currChar != 0 {
		identifier.WriteRune(l.currChar)
		if err := l.readChar(); err != nil {
			return "", err
		}
	}

	return identifier.String(), nil
}

func (l *Lexer) isLineEnd() bool {
	return l.currChar == 0 ||
		l.currChar == '\n' ||
		l.currChar == '\r'
}
